#include "stdafx.h"
#include "Cave.h"

#include "Filesystem.h"

Cave::Cave(Cvekas::RendererPtr renderer, Cvekas::VectorBatchPtr vector_batch)
: renderer(renderer), vector_batch(vector_batch), 
  camera(Vector3(0.0f, 0.0f, -0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 1.0f, 0.0f))
{
}

Cave::~Cave()
{
}

void Cave::init(boost::weak_ptr<Demo> owner)
{
	this->owner = owner;

	MeshPtr cave_mesh = MeshPtr(new Mesh(Filesystem::redirect("FractalSphere.cmf")));
	EffectPtr cave_effect = video_device->getEffectManager()->load("Cave", Filesystem::redirect("Cave.fx"));
	cave_effect->getAutoParams()->addDynamic("light_color", &light_color);
	cave_effect->getAutoParams()->addDynamic("light_pos", &light_pos);
	cave_effect->getAutoParams()->addDynamic("light_attenuation", &light_attenuation);
	light_color = Vector3(0.41f, 0.42f, 0.43f);
	cave = RenderableMeshPtr(new RenderableMesh(cave_mesh, cave_effect, EffectParamsPtr(), 1));

	MeshPtr cube_mesh = MeshPtr(new Mesh(Filesystem::redirect("Cube.cmf")));
	cube_effect = video_device->getEffectManager()->load("Cube", Filesystem::redirect("Cube.fx"));
	cubes_light_pos = cube_effect->getParamByName("light_pos");
	cubes_light_color = cube_effect->getParamByName("light_color");
	cubes_light_attenuation = cube_effect->getParamByName("light_attenuation");

	for(uint i = 0; i < cube_count; ++i)
	{
		cubes[i] = RenderableMeshPtr(new RenderableMesh(cube_mesh, cube_effect, EffectParamsPtr(), 0));
	}

	cube_size = 0.5f;
	cube_size_ratio = 1.0f;
	cube_distance = 0.0f;
	cube_distance_ratio = 1.0f;
	cube_rotation = Vector3(0.0f, 0.0f, 0.0f);
	cube_rotation_delta = Vector3(0.0f, 0.0f, 0.0f);

	fullscreen_rect.push_back(Vector2(0.0f, 0.0f));
	fullscreen_rect.push_back(Vector2(1024.0f, 0.0f));
	fullscreen_rect.push_back(Vector2(1024.0f, 576.0f));
	fullscreen_rect.push_back(Vector2(0.0f, 576.0f));
}

void Cave::cubeFractal(const Vector3& pos, float cube_size, float cube_size_ratio, float cube_distance, 
					   float cube_distance_ratio, const Vector3& cube_rotation, const Vector3& cube_rotation_delta, 
					   int depth)
{
	if(depth == 0)
		return;

	cubes[cube_idx]->setPosition(pos);
	cubes[cube_idx]->setScale(Vector3(cube_size, cube_size, cube_size));
	cubes[cube_idx]->setRotation(cube_rotation);
	cube_idx++;

	Vector3 vec1(0.0f, 0.0f, cube_distance);
	Vector3 vec2(0.0f, 0.0f, -cube_distance);
	Vector3 vec3(0.0f, cube_distance, 0.0f);
	Vector3 vec4(0.0f, -cube_distance, 0.0f);
	Vector3 vec5(cube_distance, 0.0f, 0.0f);
	Vector3 vec6(-cube_distance, 0.0f, 0.0f);

	cubeFractal(pos + vec1, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
	cubeFractal(pos + vec2, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
	cubeFractal(pos + vec3, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
	cubeFractal(pos + vec4, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
	cubeFractal(pos + vec5, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
	cubeFractal(pos + vec6, cube_size * cube_size_ratio, cube_size_ratio, cube_distance * cube_distance_ratio,
		cube_distance_ratio, cube_rotation + cube_rotation_delta, cube_rotation_delta, depth - 1);
}

void Cave::close()
{
}

void Cave::update(float t)
{
	if(t <= 15.9f)
	{
		light_attenuation = smoothstep(1.0f, 0.014f, transformT(11.0f, 12.8f, t));

		cube_size = smoothstep(0.0f, 0.3f, transformT(13.5f, 14.7f, t));
		cube_distance = smoothstep(0.0f, 0.6f, transformT(15.0f, 15.9f, t));
	}
	if(t >= 15.9f && t <= 22.4f)
	{
		cube_size_ratio = smoothstep(1.0f, 0.6f, transformT(17.6f, 17.95f, t));
		cube_distance_ratio = smoothstep(1.0f, 0.40f, transformT(18.7f, 19.4f, t));
		cube_distance = smoothstep(0.6f, 1.3f, transformT(18.7f, 19.4f, t));

		cube_rotation_delta.x = smoothstep(0.0f, pi / 4.0f, transformT(20.0f, 20.7f, t));
		cube_rotation_delta.y = smoothstep(0.0f, pi / 4.0f, transformT(20.7f, 21.5f, t));
		cube_rotation_delta.z = smoothstep(0.0f, pi / 4.0f, transformT(21.55f, 22.4f, t));
	}
	if(t >= 22.5f && t <= 24.9f)
	{
		cube_rotation_delta.x = smoothstep(pi / 4.0f, 0.0f, transformT(22.55f, 23.2f, t));
		cube_rotation_delta.y = smoothstep(pi / 4.0f, 0.0f, transformT(23.2f, 24.0f, t));
		cube_rotation_delta.z = smoothstep(pi / 4.0f, 0.0f, transformT(24.05f, 24.90f, t));
	}

	if(t >= 25.0f)
	{
		cube_rotation.x = cube_rotation.y = cube_rotation.x = smoothstep(0.0f, pi / 4.0f, transformT(25.0f, 25.7f, t));
		cube_size = smoothstep(0.3f, 0.15f, transformT(25.7f, 26.6f, t));
		cube_distance = smoothstep(1.3f, 0.8f, transformT(26.6f, 27.5f, t));
	}

	light_pos = Vector3(3.0f, 3.0f, 3.0f);
	//if(t >= 28.0f)
	//{
		light_pos.y = smoothstep(3.0f, -3.0f, transformT(14.05f, 29.8f, t));
	//}

	if(t >= 35.0f)
	{
		light_attenuation = smoothstep(0.014f, 0.00001f, transformT(36.0f, 39.0f, t));
		float alpha = smoothstep(0.0f, 1.0f, transformT(38.5f, 39.2f, t));
		Color c(255, 255, 255, (int)(alpha * 255.0f));
		vector_batch->drawPolygon(2, fullscreen_rect, c);
	}

	light_pos.x += sin(t*3.4f) * 0.8f;
	light_pos.y += cos(t*3.7f) * 0.6f;
	light_pos.z += sin(t*3.2f) * 0.6f;
	
	Vector3 camera_pos = Vector3(-sin(t/4.1f) * 2.0f + cos(t/2.9f) * 2.0f, -cos(t/6.5f) * 2.0f, -sin(t/6.2f) * cos(t/5.7f));
	camera.setPosition(camera_pos);
	camera.setDirection(-camera_pos);

	cube_idx = 0;
	cubeFractal(Vector3(sin(t/1.8f) / 12.0f, cos(t/2.0f) / 10.5f, cos(t/2.5f) / 11.2f), cube_size, cube_size_ratio, cube_distance, cube_distance_ratio,
		cube_rotation, cube_rotation_delta, cube_depth);
}	

void Cave::render(float t)
{
	Vector3 cubes_color(0.5f, 0.4f, 0.3f);
	cube_effect->setParamValue(cubes_light_pos, (void*)&light_pos, 12);
	cube_effect->setParamValue(cubes_light_color, (void*)&cubes_color, 12);
	cube_effect->setParamValue(cubes_light_attenuation, light_attenuation * 2.0f);
	
	video_device->getRenderingParams().setCamera(camera);

	video_device->setClearColor(Color(0, 0, 0));

	video_device->beginScene();
	renderer->prepare(*cave);
	if(t < 15.0f)
		renderer->prepare(*cubes[0]);
	else
	{
		if(t < 17.6f)
		{
			for(uint i = 1; i < cube_count; ++i)
			{
				if(i%8 != 0)
					renderer->prepare(*cubes[i]);
			}
		}
		else
		{
			for(uint i = 0; i < cube_count; ++i)
				renderer->prepare(*cubes[i]);
		}
	}
	vector_batch->present();
	renderer->render();
	video_device->endScene();

	renderer->endFrame();
}